package com.hex.ds.hdtp.core.app.script.test.handler.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.hex.ds.hdtp.core.app.common.enums.BasicListStatus;
import com.hex.ds.hdtp.core.app.common.enums.CrossDbContrastInfo;
import com.hex.ds.hdtp.core.app.common.enums.ScriptTableConsanguinityFrom;
import com.hex.ds.hdtp.core.app.common.util.FileEncodeUtil;
import com.hex.ds.hdtp.core.app.common.util.JaveShellUtil;
import com.hex.ds.hdtp.core.app.data.table.dto.response.TableTransferListResponseDto;
import com.hex.ds.hdtp.core.app.data.table.service.ITableTransferListService;
import com.hex.ds.hdtp.core.app.metadata.service.ISourceConfInfoService;
import com.hex.ds.hdtp.core.app.metadata.service.ISourceInfoService;
import com.hex.ds.hdtp.core.app.script.common.dto.response.ScriptTableConsanguinityResponseDto;
import com.hex.ds.hdtp.core.app.script.common.service.IScriptTableConsanguinityService;
import com.hex.ds.hdtp.core.app.script.test.converter.IScriptDataTestListConverter;
import com.hex.ds.hdtp.core.app.script.test.converter.IScriptDataTestResultConverter;
import com.hex.ds.hdtp.core.app.script.test.dto.request.ScriptTestDataAnalysisRequestDto;
import com.hex.ds.hdtp.core.app.script.test.dto.response.ScriptDataTestListResponseDto;
import com.hex.ds.hdtp.core.app.script.test.dto.response.ScriptDataTestResultResponseDto;
import com.hex.ds.hdtp.core.app.script.test.dto.response.ScriptTestDataAnalysisResponseDto;
import com.hex.ds.hdtp.core.app.script.test.enums.ScriptDataAnalysisResultEnum;
import com.hex.ds.hdtp.core.app.script.test.handler.IScriptDataTestHandlerService;
import com.hex.ds.hdtp.core.app.script.test.service.IScriptDataTestListService;
import com.hex.ds.hdtp.core.app.script.test.service.IScriptDataTestResultService;
import com.hex.ds.hdtp.core.app.script.test.service.IScriptDataTestTaskService;
import com.hex.ds.hdtp.core.app.script.test.service.IScriptTestDataAnalysisService;
import com.hex.ds.hdtp.core.app.script.transfer.dto.response.ScriptConvertTargetListResponseDto;
import com.hex.ds.hdtp.core.app.script.transfer.service.IScriptConvertTargetListService;
import com.hex.ds.hdtp.core.inf.common.exception.SourceJDBCException;
import com.hex.ds.hdtp.core.inf.common.exception.SourceJDBCExceptionDetail;
import com.hex.ds.hdtp.core.inf.common.utils.JdbcConnectUtil;
import com.hex.ds.hdtp.core.inf.data.contrast.service.impl.DataInfoSelectHandlerContext;
import com.hex.ds.hdtp.core.inf.metadata.po.SourceConfInfoPo;
import com.hex.ds.hdtp.core.inf.metadata.po.SourceInfoPo;
import com.hex.ds.hdtp.core.inf.script.test.po.ScriptDataTestTaskPo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.map.LinkedMap;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLException;
import java.util.*;

/**
 * @Package: com.hex.ds.hdtp.core.app.script.test.service.impl
 * @ClassName ScriptDataTestTaskService
 * @Description: 带数据测试任务-业务接口实现
 * @Author: wz.li
 * @Date 2023/8/1 16:23
 * @Version v2.0.2
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy, @Autowired})
public class ScriptDataTestHandlerService implements IScriptDataTestHandlerService {

    private final IScriptDataTestResultService scriptDataTestResultService;

    private final IScriptDataTestResultConverter scriptDataTestResultConverter;

    private final IScriptDataTestTaskService scriptDataTestTaskService;

    private final IScriptTableConsanguinityService scriptTableConsanguinityService;

    private final IScriptDataTestListService scriptDataTestListService;

    private final IScriptTestDataAnalysisService scriptTestDataAnalysisService;

    private final IScriptDataTestListConverter scriptDataTestListConverter;

    private final JaveShellUtil javeShellUtil;

    private final DataInfoSelectHandlerContext dataInfoSelectHandlerContext;

    private final ITableTransferListService tableTransferListService;

    private final ISourceInfoService sourceInfoService;

    private final ISourceConfInfoService sourceConfInfoService;

    private final IScriptConvertTargetListService scriptConvertTargetListService;

    @Value("${python.exe}")
    private String pythonExe;

    @Value("${servers.isUse}")
    private Boolean isUse;

    private static final String DATE8_FROMAT = "yyyyMMdd";

    private final  String LINEFEED = System.lineSeparator();

    /*
     * @Method: test <br>
     * @Param: [scriptDataTestTaskPo, scriptDataTestResult] <br>
     * @Return: void <br>
     * @Description：如果点击了数据分析，则测试需要等待数据分析结束后才能做测试<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/7 20:11 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public void test(ScriptDataTestTaskPo scriptDataTestTaskPo, ScriptDataTestResultResponseDto scriptDataTestResult) {
        Map<String, String> resultMap = new HashMap<>();
        try {
            scriptDataTestResult.setStartTime(DateUtil.now());
            // ① 数据分析
            toDataAlignment(scriptDataTestTaskPo, scriptDataTestResult.getScriptTestListPkId(), scriptDataTestResult.getScriptConvertListPkId(), resultMap);
            // ② 执行脚本测试
            // resultMap = toScriptTest(scriptDataTestResult);
            // TODO 这里判断文件为sql脚本则用JDBC方式执行SQL文件，后期考虑使用策略模式实现
            if (StrUtil.endWithIgnoreCase(scriptDataTestResult.getScriptName(), "sql")) {
                String sqlContent = getFileContent(scriptDataTestResult.getScriptAbsolutePath());
                ScriptConvertTargetListResponseDto scriptConvertTargetListResponseDto = scriptConvertTargetListService.queryById(scriptDataTestResult.getScriptConvertListPkId());
                List<SourceConfInfoPo> sourceConfInfoPoList = sourceConfInfoService.queryListBySourceCode(scriptConvertTargetListResponseDto.getTargetConnCode());
                SourceJDBCException jdbcException = JdbcConnectUtil.runSqls(sourceConfInfoPoList, sqlContent, false);
                resultMap.putIfAbsent("SHELL_RESULT", jdbcException.isExecutionSuccess() ? "0" : "1");
                StringBuilder messageBuilder = new StringBuilder();
                messageBuilder.append(StrUtil.format("执行SQL脚本完成，总共 {} 段SQL，成功 {} 段，失败 {} 段。{}", jdbcException.getTotalSqlCount(), jdbcException.getSuccessCount(), jdbcException.getFailureCount(), LINEFEED + LINEFEED));
                for (int i = 0; i < jdbcException.getJdbcDetail().size(); i++) {
                    SourceJDBCExceptionDetail jdbcDetail = jdbcException.getJdbcDetail().get(i);
                    messageBuilder.append(StrUtil.format("第 {} 段SQL执行{}: {}", jdbcDetail.getIndex(), jdbcDetail.isSuccess() ? "成功" : "失败", LINEFEED));
                    messageBuilder.append(StrUtil.format("  {}{}", jdbcDetail.getSql(), LINEFEED + LINEFEED));
                    if (!jdbcDetail.isSuccess()) {
                        messageBuilder.append(StrUtil.format("第 {} 段SQL失败原因: {}{}{}", jdbcDetail.getIndex(), LINEFEED, jdbcDetail.getErrorMessage(), LINEFEED + LINEFEED));
                    }
                }
                resultMap.put("SHELL_ERR_RESULT", messageBuilder.toString());
            } else {
                resultMap = toScriptTest(scriptDataTestResult);
            }
        } catch (Exception e) {
            log.error("脚本【{}】测试失败: {}", scriptDataTestResult.getScriptName(), e.getMessage(), e);
            resultMap.putIfAbsent("SHELL_RESULT", "1");
            resultMap.putIfAbsent("SHELL_ERR_RESULT", "脚本执行异常：" + e.getMessage());
        } finally {
            afterToScriptTest(scriptDataTestResult, resultMap);
        }
    }


    /**
     * @Method getFileContent
     * @Param filePath
     * @Return java.lang.String
     * @Description 读取指定文件内容
     * @Author Yin.Yang
     * @Date 2024/3/13 17:52
     * @Version V1.0
     */
    private String getFileContent(String filePath) {
        String convertFileAbsolutePath = FilenameUtils.separatorsToUnix(filePath);
        if (FileUtil.exist(convertFileAbsolutePath)) {
            return FileUtil.readString(convertFileAbsolutePath, FileEncodeUtil.getFileEncode(convertFileAbsolutePath));
        }
        return "";
    }

    /*
     * @Method: dataAlignment <br>
     * @Param: [scriptDataTestTaskPo] <br>
     * @Return: void <br>
     * @Description：数据分析<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/8 10:30 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public void dataAlignment(ScriptDataTestTaskPo scriptDataTestTaskPo) {
        List<ScriptDataTestListResponseDto> scriptDataTestList = scriptDataTestListService.queryListByTaskId(scriptDataTestTaskPo.getPkId());
        for (ScriptDataTestListResponseDto scriptDataTestListResponseDto : scriptDataTestList) {
            doDataAlignment(scriptDataTestTaskPo, scriptDataTestListResponseDto);
        }
    }

    private void doDataAlignment(ScriptDataTestTaskPo scriptDataTestTaskPo, ScriptDataTestListResponseDto scriptDataTestList) {
        try {
            toDataAlignment(scriptDataTestTaskPo, scriptDataTestList.getPkId(), scriptDataTestList.getScriptConvertListPkId(), new HashMap());
        } catch (Exception e) {
            log.error("脚本【{}】的数据表【{}】/【{}】数据分析异常：{}", scriptDataTestList.getScriptName(),
                    scriptDataTestList.getSourceTableNames(), scriptDataTestList.getTargetTableNames(), e.getMessage(), e);
        }
    }

    /*
     * @Method: dataAlignment <br>
     * @Param: [scriptDataTestTaskPo, scriptTestListPkId] <br>
     * @Return: void <br>
     * @Description：数据分析<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/7 20:13 <br>
     * @Version： V2.0.2<br>
     */
    private void toDataAlignment(ScriptDataTestTaskPo scriptDataTestTaskPo, String scriptTestListPkId, String scriptConvertListPkId, Map<String, String> resultMap) {
        // 这里或将用于从脚本迁移中获取表血缘关系
        // final ScriptConvertTargetListResponseDto responseDto = scriptConvertTargetListService.queryById(scriptConvertListPkId);
        List<ScriptTableConsanguinityResponseDto> scriptTableConsanguinitys =
                scriptTableConsanguinityService.queryList(scriptTestListPkId, ScriptTableConsanguinityFrom.SCRIPT_TEST.getName());
        for (DateTime dateTime : scriptDataTestTaskPo.getBizDateList()) {
            if (CollUtil.isEmpty(scriptTableConsanguinitys)) {
                insertResultMap(ScriptDataAnalysisResultEnum.SUCCESS.getName(), "数据分析成功，没有需要检测的表", resultMap);
                insertScriptTestAlignment(scriptDataTestTaskPo.getPkId(), scriptTestListPkId, "", "", "", "", DateUtil.format(dateTime, DATE8_FROMAT), resultMap.get("ALIGNMENT_RESULT"), resultMap.get("ALIGNMENT_ERR_RESULT"));
            }
            for (ScriptTableConsanguinityResponseDto scriptTableConsanguinity : scriptTableConsanguinitys) {
                    resultMap.clear();
                    // ① 脚本测试前检查（是否建表、字段总数）
                    Boolean isbeforeTest = beforToScriptTest(scriptTableConsanguinity, resultMap);
                    // ② 数据检查（记录数是否一致）
                    Boolean isDataConsistency = dataSynchronizationCheck(scriptTableConsanguinity, resultMap, DateUtil.format(dateTime, DATE8_FROMAT));
                    // ③ 数据铺底(源表中不存在，脚本不测试，目标表中数据与源表中数据一致则不做数据同步)
                    // dataSynchronization(scriptDataTestTaskPo.getBizDateList(), scriptTableConsanguinity);
                    // ④ 如果之前的检查错误，则将报错信息插入数据分析表
                    if (isDataConsistency && isbeforeTest) {
                        insertResultMap(ScriptDataAnalysisResultEnum.SUCCESS.getName(), "数据分析成功", resultMap);
                    } else {
                        insertResultMap(ScriptDataAnalysisResultEnum.FAIL.getName(), "数据分析失败", resultMap);
                    }
                    // 获取相关信息并操作数据库
                    String scriptName = scriptTableConsanguinity.getScriptName();
                    String transferTableName = scriptTableConsanguinity.getTableName();
                    String targetPkId = sourceInfoService.queryBySourceCode(scriptTableConsanguinity.getTargetCode()).getPkId();
                    String tableName = getTransferBeforeTable(transferTableName.split("\\.")[0], transferTableName.split("\\.")[1], targetPkId);
                    String tableType = scriptTableConsanguinity.getTableType();
                    String statusAnalysisResult = String.valueOf(resultMap.get("ALIGNMENT_RESULT"));
                    String errorAnalysisMessage = String.valueOf(resultMap.get("ALIGNMENT_ERR_RESULT"));
                    insertScriptTestAlignment(scriptDataTestTaskPo.getPkId(), scriptTestListPkId, scriptName, tableName, transferTableName, tableType, DateUtil.format(dateTime, DATE8_FROMAT), statusAnalysisResult, errorAnalysisMessage);
                }
            }
    }

    /*
     * @Method: beforToScriptTest <br>
     * @Param: [scriptDataTestListResponseDto] <br>
     * @Return: void <br>
     * @Description：脚本测试前检查<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/4 17:47 <br>
     * @Version： V2.0.2<br>
     */
    private Boolean beforToScriptTest(ScriptTableConsanguinityResponseDto scriptTableConsanguinity, Map resultMap) {
        try {
            Boolean createFlag = isCreateTable(scriptTableConsanguinity, resultMap);
            Boolean countFlag = createFlag && isFieldCount(scriptTableConsanguinity, resultMap);
            return createFlag && countFlag;
        } catch (Exception e) {
            String errorMsg = StrUtil.format("脚本测试前检查异常：{}", e.getMessage(), e);
            insertResultMap("脚本测试前检查失败", errorMsg, resultMap);
            return false;
        }
    }


    private void dataSynchronization(List<DateTime> dateTimeList, ScriptTableConsanguinityResponseDto scriptTableConsanguinity) {
        for (DateTime dateTime : dateTimeList) {
            String bizDate = DateUtil.format(dateTime, DATE8_FROMAT);
        }
    }

    /**
     * @param scriptTableConsanguinity
     * @Method dataSynchronizationCheck
     * @Return Boolean
     * @Description 判断转换前源表与转换后源表的记录数是否一致
     * @Author Yin.Yang
     * @Date 2023/10/23 14:39
     * @Version V1.0
     */
    private Boolean dataSynchronizationCheck(ScriptTableConsanguinityResponseDto scriptTableConsanguinity, Map resultMap, String bizDate) {
        try {
            // 源库和比对库的类型
            String sourceType = getConnType(scriptTableConsanguinity.getSourceCode());
            String targetType = getConnType(scriptTableConsanguinity.getTargetCode());
            String targetPkId = sourceInfoService.queryBySourceCode(scriptTableConsanguinity.getTargetCode()).getPkId();
            // 转换后和转换前的表信息
            String transferDbName = scriptTableConsanguinity.getTableName().split("\\.")[0];
            String transferTableName = scriptTableConsanguinity.getTableName().split("\\.")[1];
            String tableDbName = getTransferBeforeTable(transferDbName, transferTableName, targetPkId).split("\\.")[0];
            String tableName = getTransferBeforeTable(transferDbName, transferTableName, targetPkId).split("\\.")[1];
            // 转换后和转换前的表记录数 TODO 添加分区字段where条件查询
            Long sourceDataNum = dataInfoSelectHandlerContext.getDataInfoService(sourceType)
                    .selectDataNum(scriptTableConsanguinity.getSourceCode(), tableDbName, tableName, "", "");
            Long transferDataNum = dataInfoSelectHandlerContext.getDataInfoService(targetType)
                    .selectDataNum(scriptTableConsanguinity.getTargetCode(), transferDbName, transferTableName, "", "");
            if (!sourceDataNum.equals(transferDataNum)) {
                String errorMsg = StrUtil.format("测试记录总数失败，转换后源表 {}.{} 记录数不匹配: ", transferDbName, transferTableName);
                insertResultMap(ScriptDataAnalysisResultEnum.DATA_COUNT_FAIL.getName(), errorMsg, resultMap);
            }
            return sourceDataNum.equals(transferDataNum);
        } catch (Exception e) {
            String errorMsg = StrUtil.format("测试记录总数发生异常: {}", e.getMessage(), e);
            insertResultMap(ScriptDataAnalysisResultEnum.DATA_COUNT_FAIL.getName(), errorMsg, resultMap);
            return false;
        }
    }

    /*
     * @Method: toScriptTest <br>
     * @Param: [scriptDataTestListResponseDto] <br>
     * @Return: void <br>
     * @Description：执行脚本测试<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/4 17:47 <br>
     * @Version： V2.0.2<br>
     */
    private Map toScriptTest(ScriptDataTestResultResponseDto scriptDataTestResult) {
        String shell = StrUtil.format("{} {} {}", pythonExe, scriptDataTestResult.getScriptAbsolutePath(), scriptDataTestResult.getBizDate());
        if (isUse) {
            return javeShellUtil.executeScriptShell(shell);
        }
        return JaveShellUtil.ExecCommand(shell);
    }

    /**
     * @Package: com.hex.ds.hdtp.core.app.script.test.service.impl
     * @ClassName ScriptDataTestHandlerService
     * @Description: 脚本测试后数据更新
     * @Author: wz.li
     * @Date 2023/8/7 9:28
     * @Version v2.0.2
     **/
    private void afterToScriptTest(ScriptDataTestResultResponseDto scriptDataTestResult, Map resultMap) {
        updateTestScriptResult(scriptDataTestResult, resultMap);
        updateTestScriptTask(scriptDataTestResult, resultMap);
    }

    /*
     * @Method: updateTestScriptResult <br>
     * @Param: [scriptDataTestResult, resultMap] <br>
     * @Return: void <br>
     * @Description：更新测试结果信息<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/7 18:22 <br>
     * @Version： V2.0.2<br>
     */
    private void updateTestScriptResult(ScriptDataTestResultResponseDto scriptDataTestResult, Map resultMap) {
        scriptDataTestResult.setEndTime(DateUtil.now());
        String status = String.valueOf(resultMap.get(JaveShellUtil.SHELL_RESULT)).equals("0") ? BasicListStatus.SUCCESS.name() : BasicListStatus.FAIL.name();
        scriptDataTestResult.setStatus(status);
        scriptDataTestResult.setErrorMessage(String.valueOf(resultMap.get(JaveShellUtil.SHELL_ERR_RESULT)));
        scriptDataTestResultService.modifyById(scriptDataTestResultConverter.toScriptDataTestResultRequestDto(scriptDataTestResult));
    }

    /*
     * @Method: updateTestScriptTask <br>
     * @Param: [scriptDataTestResult, resultMap] <br>
     * @Return: void <br>
     * @Description：更新测试任务信息<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/7 18:22 <br>
     * @Version： V2.0.2<br>
     */
    private void updateTestScriptTask(ScriptDataTestResultResponseDto scriptDataTestResult, Map resultMap) {
        if (scriptDataTestResult.getStatus().equals((BasicListStatus.SUCCESS.name()))) {
            scriptDataTestTaskService.modifySuccessById(scriptDataTestResult.getTaskPkId());
        } else {
            scriptDataTestTaskService.modifyFailNumById(scriptDataTestResult.getTaskPkId());
        }
    }

    /**
     * @param scriptTableConsanguinity
     * @Method contrastCreateTable
     * @Return Boolean
     * @Description 判断是否建表
     * @Author Yin.Yang
     * @Date 2023/9/26 14:58
     * @Version V1.0
     */
    private Boolean isCreateTable(ScriptTableConsanguinityResponseDto scriptTableConsanguinity, Map resultMap) {
        Boolean result = false;
        String dbName = scriptTableConsanguinity.getTableName().split("\\.")[0];
        String tableName = scriptTableConsanguinity.getTableName().split("\\.")[1];
        String targetType = getConnType(scriptTableConsanguinity.getTargetCode());
        String targetPkId = sourceInfoService.queryBySourceCode(scriptTableConsanguinity.getTargetCode()).getPkId();
        try {
            Boolean isTableTransfer = !getTransferBeforeTable(dbName, tableName, targetPkId).isEmpty();
            result = isTableTransfer && dataInfoSelectHandlerContext.getDataInfoService(targetType).selectTableExist(scriptTableConsanguinity.getTargetCode(), dbName, tableName);
            if (!isTableTransfer) {
                String errorMsg = StrUtil.format("判断是否建表失败，{} 中表 {}.{} 未进行表迁移 ", targetType, dbName, tableName);
                insertResultMap(ScriptDataAnalysisResultEnum.CREATE_TABLE_FAIL.getName(), errorMsg, resultMap);
            }
            if (isTableTransfer ^ result) {
                String errorMsg = StrUtil.format("判断是否建表失败，{} 中表 {}.{} 已进行表迁移，但目标库中不存在 ", targetType, dbName, tableName);
                insertResultMap(ScriptDataAnalysisResultEnum.CREATE_TABLE_FAIL.getName(), errorMsg, resultMap);
            }
        } catch (Exception e) {
            String errorMsg = StrUtil.format("判断是否建表操作异常，{} 中表 {}.{} ", targetType, dbName, tableName) + e.getMessage();
            insertResultMap(ScriptDataAnalysisResultEnum.CREATE_TABLE_FAIL.getName(), errorMsg, resultMap);
        }
        return result;
    }

    /**
     * @param scriptTableConsanguinity
     * @Method isFieldCount
     * @Return Boolean
     * @Description 测试转换前与转换后表的字段总数是否一致
     * @Author Yin.Yang
     * @Date 2023/10/23 14:35
     * @Version V1.0
     */
    public Boolean isFieldCount(ScriptTableConsanguinityResponseDto scriptTableConsanguinity, Map resultMap) {
        try {
            // 源库和比对库的类型
            String sourceType = getConnType(scriptTableConsanguinity.getSourceCode());
            String sourceCode = scriptTableConsanguinity.getSourceCode();
            String targetType = getConnType(scriptTableConsanguinity.getTargetCode());
            String targetCode = scriptTableConsanguinity.getTargetCode();
            String targetPkId = sourceInfoService.queryBySourceCode(targetCode).getPkId();
            // 转换后和转换前的表信息
            String transferDbName = scriptTableConsanguinity.getTableName().split("\\.")[0];
            String transferTableName = scriptTableConsanguinity.getTableName().split("\\.")[1];
            String tableDbName = getTransferBeforeTable(transferDbName, transferTableName, targetPkId).split("\\.")[0];
            String tableName = getTransferBeforeTable(transferDbName, transferTableName, targetPkId).split("\\.")[1];
            // 获取转换后和转换前表的字段数
            Long sourceTransferFieldCount = getFieldCount(targetType, targetCode, transferDbName, transferTableName);
            Long sourceFieldCount = getFieldCount(sourceType, sourceCode, tableDbName, tableName);
            // 如果比对双方不为相同类型且涉及到IMPALA(大数据仓库)类型，则IMPALA类型的表长度减n处理
            if (shouldAdjustForImpala(sourceType, targetType)) {
                sourceFieldCount = adjustForImpala(sourceFieldCount, sourceType, sourceCode, targetType, targetCode, tableDbName, tableName, transferDbName, transferTableName);
            } else if (shouldAdjustForImpala(targetType, sourceType)) {
                sourceTransferFieldCount = adjustForImpala(sourceTransferFieldCount, targetType, targetCode, sourceType, sourceCode, transferDbName, transferTableName, tableDbName, tableName);
            }
            if (sourceTransferFieldCount != null && !sourceTransferFieldCount.equals(sourceFieldCount)) {
                String errorMsg = StrUtil.format("测试字段总数失败，转换后源表 {}.{} 字段数不匹配: ", transferDbName, transferTableName);
                insertResultMap(ScriptDataAnalysisResultEnum.FILED_NUM_FAIL.getName(), errorMsg, resultMap);
                return false;
            }
        } catch (Exception e) {
            String errorMsg = StrUtil.format("测试字段总数发生异常: {}", e.getMessage(), e);
            insertResultMap(ScriptDataAnalysisResultEnum.FILED_NUM_FAIL.getName(), errorMsg, resultMap);
            return false;
        }
        return true;
    }

    /**
     * @Method getSourceTable
     * @Return java.lang.String
     * @Description 获取表转换前的表
     * @Author Yin.Yang
     * @Date 2023/10/26 14:50
     * @Version V1.0
     */
    private String getTransferBeforeTable(String transferDbName, String transferTableName, String targetPkId) {
        TableTransferListResponseDto tableTransferInfo = tableTransferListService.queryLatestTransferTableByTarget(transferDbName, transferTableName, targetPkId);
        return tableTransferInfo == null ? "" : tableTransferInfo.getSourceTable();
    }

    /**
     * @param connType
     * @param connCode
     * @param databaseName
     * @param tableName
     * @Method getFieldCount
     * @Return Long
     * @Description 获取字段总数
     * @Author Yin.Yang
     * @Date 2023/10/23 14:34
     * @Version V1.0
     */
    private Long getFieldCount(String connType, String connCode, String databaseName, String tableName) {
        try {
            return dataInfoSelectHandlerContext.getDataInfoService(connType)
                    .selectAllFieldNum(connCode, databaseName, tableName);
        } catch (SQLException e) {
            log.error("获取表字段总数发生异常: {}", e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * @param mainFieldCount
     * @param mainType
     * @param otherType
     * @param mainDbName
     * @param mainTableName
     * @param otherDbName
     * @param otherTableName
     * @Method adjustForImpala
     * @Return Long
     * @Description 调整字段总数(大数据类型数据库需要)
     * @Author Yin.Yang
     * @Date 2023/10/23 14:34
     * @Version V1.0
     */
    private Long adjustForImpala(Long mainFieldCount, String mainType, String mainCode, String otherType, String otherCode, String mainDbName, String mainTableName, String otherDbName, String otherTableName) {
        try {
            List<String> partitionFields = dataInfoSelectHandlerContext.getDataInfoService(mainType)
                    .selectPartitionField(mainCode, mainDbName, mainTableName);
            List<String> allFields = dataInfoSelectHandlerContext.getDataInfoService(otherType)
                    .selectFieldName(otherCode, otherDbName, otherTableName);
            return dealFieldNum(mainFieldCount, partitionFields, allFields);
        } catch (SQLException e) {
            log.error("处理大数据类型数据库字段总数发生异常: {}", e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * @param mainType
     * @param otherType
     * @Method shouldAdjustForImpala
     * @Return Boolean
     * @Description 判断数据库类型是否相等且是否涉及到IMPALA(大数据仓库)类型
     * @Author Yin.Yang
     * @Date 2023/10/27 14:52
     * @Version V1.0
     */
    private Boolean shouldAdjustForImpala(String mainType, String otherType) {
        return !mainType.equals(otherType) &&
                (CrossDbContrastInfo.IMPALA.getName().equals(mainType.toUpperCase()) || CrossDbContrastInfo.GAUSS.getName().equals(mainType.toUpperCase()));
    }


    /**
     * @param testScriptTaskPkId
     * @param scriptTestListPkId
     * @param scriptName
     * @param bizDate
     * @param statusAnalysisResult
     * @param errorAnalysisMessage
     * @Method insertScriptTestAlignment
     * @Return void
     * @Description 将脚本测试分析数据插入表中
     * @Author Yin.Yang
     * @Date 2023/10/26 18:31
     * @Version V1.0
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void insertScriptTestAlignment(String testScriptTaskPkId, String scriptTestListPkId, String scriptName, String tableName, String transferTableName, String tableType, String bizDate, String statusAnalysisResult, String errorAnalysisMessage) {
        ScriptTestDataAnalysisRequestDto scriptTestDataAnalysisRequestDto = scriptDataTestListConverter.toScriptTestDataAnalysisRequestDto(
                testScriptTaskPkId, scriptTestListPkId, scriptName, transferTableName, tableType, bizDate);
        ScriptTestDataAnalysisResponseDto result = CollUtil.getFirst(scriptTestDataAnalysisService.queryList(scriptTestDataAnalysisRequestDto));
        if (result == null) {
            scriptTestDataAnalysisRequestDto.setSourceTableName(tableName);
            scriptTestDataAnalysisRequestDto.setContrastAnalysisResult(statusAnalysisResult);
            scriptTestDataAnalysisRequestDto.setOneselfAnalysisResult(errorAnalysisMessage);
            scriptTestDataAnalysisService.add(scriptTestDataAnalysisRequestDto);
        } else {
            scriptTestDataAnalysisRequestDto.setPkId(result.getPkId());
            scriptTestDataAnalysisRequestDto.setSourceTableName(tableName);
            scriptTestDataAnalysisRequestDto.setContrastAnalysisResult(statusAnalysisResult);
            scriptTestDataAnalysisRequestDto.setOneselfAnalysisResult(errorAnalysisMessage);
            scriptTestDataAnalysisService.modifyById(scriptTestDataAnalysisRequestDto);
        }
    }


    /**
     * @param fieldNum
     * @param partitionFieldList
     * @param allFieldNameList
     * @Method dealFieldNum
     * @Param
     * @Return java.lang.Long
     * @Description 处理因分区字段场景导致的字段数量问题
     * @Author Yin.Yang
     * @Date 2023/9/26 14:58
     * @Version V1.0
     */
    private Long dealFieldNum(Long fieldNum, List<String> partitionFieldList, List<String> allFieldNameList) {
        for (String partitionField : partitionFieldList) {
            if (!allFieldNameList.contains(partitionField)) {
                fieldNum--;
            }
            for (String allField : allFieldNameList) {
                if (partitionField.equalsIgnoreCase(allField.concat(CrossDbContrastInfo.SUFFIX.getName()))) {
                    fieldNum--;
                }
            }
        }
        return fieldNum;
    }

    /**
     * @param resultMap
     * @param key
     * @param value
     * @Method insertResultMap
     * @Return void
     * @Description 非覆盖性获取结果集
     * @Author Yin.Yang
     * @Date 2023/10/26 14:17
     * @Version V1.0
     */
    private void handleResultMap(Map resultMap, String key, String value) {
        if (resultMap.get(key) == null) {
            resultMap.put(key, value);
        }
    }

    /**
     * @param status
     * @param errorMsg
     * @param resultMap
     * @Method handleTestError
     * @Return void
     * @Description 插入结果异常集
     * @Author Yin.Yang
     * @Date 2023/10/27 18:18
     * @Version V1.0
     */
    private void insertResultMap(String status, String errorMsg, Map<String, String> resultMap) {
        handleResultMap(resultMap, "ALIGNMENT_RESULT", status);
        handleResultMap(resultMap, "ALIGNMENT_ERR_RESULT", errorMsg);
        log.error(errorMsg);
    }

    /**
     * @param connCode
     * @Method getConnType
     * @Return java.lang.String
     * @Description 获取数据源类型
     * @Author Yin.Yang
     * @Date 2023/10/30 16:24
     * @Version V1.0
     */
    private String getConnType(String connCode) {
        SourceInfoPo sourceConInfo = sourceInfoService.queryBySourceCode(connCode);
        return sourceConInfo.getSourceType();
    }

}
